package com.luobd.bpm.command;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import com.luobd.bpm.common.process.DynamicStateManager;
import lombok.AllArgsConstructor;
import org.activiti.bpmn.model.*;
import org.activiti.engine.ActivitiIllegalArgumentException;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.util.ProcessDefinitionUtil;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;

import java.util.List;
import java.util.stream.Collectors;

@AllArgsConstructor
public class TaskRecallCmd implements Command<Void> {



    private final String taskId;


    @Override
    public Void execute(CommandContext commandContext) {
        if(StrUtil.isBlank(taskId)) {
            throw  new ActivitiIllegalArgumentException("任务id不能为空");
        }

        HistoryService historyService = commandContext
                .getProcessEngineConfiguration().getHistoryService();

        HistoricTaskInstance instance = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();


        // 校验
        basicCheck(commandContext,instance);
        // 获取流程模型
        BpmnModel model = ProcessDefinitionUtil.getBpmnModel(instance.getProcessDefinitionId());

        FlowElement flowElement = model.getFlowElement(instance.getTaskDefinitionKey());
        List<String> nextElementIdList = Lists.newArrayList();
        List<UserTask> nextElementTaskList = Lists.newArrayList();

        // 获取后续结点信息
        getNextElementInfo(model,flowElement,nextElementIdList,nextElementTaskList);
        //校验后续结点，是否已经完成办理
        existNextFinishedTaskCheck(commandContext,instance,nextElementTaskList);

        // 流程相关数据准备
        DynamicStateManager dynamicStateManager = new DynamicStateManager();
        List<String> recallElementIds = getRecallElementIds(commandContext, instance, nextElementIdList);
        List<ExecutionEntity> executionEntities = Lists.newArrayList();
        for (String recallElementId : recallElementIds) {
            ExecutionEntity execution = dynamicStateManager.resolveActiveExecution(instance.getProcessInstanceId(), recallElementId, commandContext);
            executionEntities.add(execution);
        }

        // 执行撤回操作
        dynamicStateManager.moveExecutionState(executionEntities,instance.getTaskDefinitionKey(),commandContext);
        return null;
    }


    private void basicCheck(CommandContext commandContext,HistoricTaskInstance instance) {
        if(instance == null) {
            throw new RuntimeException("任务不存在");
        }
        if(instance.getEndTime() == null) {
            throw new RuntimeException("任务正在执行,不需要回退");
        }
        RuntimeService runtimeService = commandContext
                .getProcessEngineConfiguration().getRuntimeService();
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(instance.getProcessInstanceId()).singleResult();
        if(processInstance == null) {
            throw new RuntimeException("该流程已经完成,无法进行回退");
        }
    }



    private void getNextElementInfo(BpmnModel model,FlowElement currentElement,
                                    List<String> nextElementIds,List<UserTask> nextTasks) {

        FlowNode flowNode = (FlowNode) currentElement;
        List<SequenceFlow> outgoingFlows = flowNode.getOutgoingFlows();
        for (SequenceFlow flow : outgoingFlows) {
            FlowElement element = model.getFlowElement(flow.getTargetRef());
            nextElementIds.add(element.getId());
            if(element instanceof UserTask) {
                UserTask userTask = (UserTask) element;
                nextTasks.add(userTask);
            } else if(element instanceof Gateway) {
                Gateway gateway = (Gateway) element;
                getNextElementInfo(model,gateway,nextElementIds,nextTasks);
            }

        }


    }




    private void existNextFinishedTaskCheck(CommandContext commandContext,
                                            HistoricTaskInstance historicTaskInstance,List<UserTask> nextUserTasks) {
        HistoryService historyService = commandContext.getProcessEngineConfiguration().getHistoryService();
        List<HistoricTaskInstance> taskInstances = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(historicTaskInstance.getProcessInstanceId())
                .taskCompletedAfter(historicTaskInstance.getEndTime()).list();
        List<String> nextUserTaskIds = nextUserTasks.stream().map(UserTask::getId).collect(Collectors.toList());
        if(CollectionUtil.isNotEmpty(taskInstances)) {
            for (HistoricTaskInstance instance : taskInstances) {
                if(nextUserTaskIds.contains(instance.getTaskDefinitionKey())) {
                    throw new RuntimeException("存在已完成下一结点任务");
                }
            }
        }

    }



    private List<String> getRecallElementIds(CommandContext commandContext,HistoricTaskInstance historicTaskInstance,
                                             List<String> nextElementIds) {

        List<String> recallElementIds = Lists.newArrayList();
        RuntimeService runtimeService = commandContext.getProcessEngineConfiguration().getRuntimeService();
        List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(historicTaskInstance.getProcessInstanceId())
                .onlyChildExecutions().list();
        if(CollectionUtil.isNotEmpty(executions)) {
            executions.forEach(v-> {
                if(nextElementIds.contains(v.getActivityId())) {
                    recallElementIds.add(v.getActivityId());
                }
            });
        }

        return recallElementIds;
    }










}
